
// --begin async,await,promise执行顺序问题 --
/*
1.宏任务和微任务
    宏任务：setTimeOut、setInterval、setImmediate
    微任务：promise.then、process.nextTick、Object.observe、MutationObserver
    【注意】Promise是同步任务
    执行顺序：
    执行 script
    进入 script 后，所有的同步任务主线程执行
    所有宏任务放入宏任务执行队列
    所有微任务放入微任务执行队列
    先清空微任务队列
    再取一个宏任务，执行，再清空任务队列
2.async、await执行顺序
    使用async定义的函数，当它被调用时，它返回的其实是一个Promise对象。可以使用then方法添加回调函数。
    当函数执行的时候，一旦遇到await就会阻塞函数体后面代码的运行，当await等待到了成功的结果后，它会将async函数剩余内容推入到微任务队列中等待执行
3.以下代码输出结果为3 1 5 2 4
    执行async2()，进入async2输出3，然后await async1()立即调用async1()并等待结果，进入async1输出1，然后执行sleep(1000)，
    await sleep(1000))在等结果，此时执行async2()后面的console.log(5)（async2是异步函数，内部阻塞时不会影响它后面的代码执行），
    await sleep(1000))等到结果后，将剩余的console.log(2)放入微任务队列，等到微任务队列里取出console.log(2)执行后，await async1()获取
    到结果，将console.log(4)放入微任务队列然后执行。
*/
function sleep(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
}

async function async1() {
    console.log(1);
    await sleep(1000);
    console.log(2);
}

async function async2() {
    console.log(3);
    await async1();
    console.log(4);
}

async2()
console.log(5)

// ---------------- end  -----------------


// ---------------begin 实现分片下载 ------------
// 1、下载操作
async function downloadFile() {
    const url = '/download';
    // 1、文件总大小
    const fileSize = await getFileSize(url);
    // 2、下载块大小
    const chunkSize = 100 * 1024 * 1024; // 100MB
    // 分块下载次数
    const chunks = Math.ceil(fileSize / chunkSize);
    // 文件名称
    const fileName = 'xxx.apk';
    // 3、分块下载并发执行
    const chunksArray = Array.from({ length: chunks }, (_, i) => i);
    const responses = await Promise.all(chunksArray.map(i => {
        downloadChunk(url, i * chunkSize, Math.min((i + 1) * chunkSize - 1, fileSize - 1))
    }));
    // 4、合并文件
    const blob = new Blob(responses, { type: 'application/octet-stream' });
    const link = document.createElement('a');
    // 5、拼接下载提示
    link.href = URL.createObjectURL(blob);
    link.download = fileName;
    link.click();
    URL.revokeObjectURL(link.href);
}

// 2、获取文件大小
async function getFileSize(url) {
    const response = await axios.head(url);
    return parseInt(response.headers['content-length'], 10);
}

// 3、分块下载
async function downloadChunk(url, start, end) {
    console.log("下载开始字节数：", start);
    const headers = {
        Range: `bytes=${start}-${end}`
    };
    const response = await axios.get(url, { headers, responseType: 'arraybuffer' });
    return response.data;
}
// ---------------end 实现分片下载 ------------


// ---------------begin await捕捉异常 ------------
function errorPromise(isThrowError = true) {
    return new Promise((resolve, reject) => {
        if (isThrowError)
            throw new Error('错误');
        resolve('ok')
    });
}

/**
 * 捕获异常是使用try/catch的方式来处理，因为await后面跟着的是Promise对象，当有异常的情况下会被Promise对象的内部
 * catch捕获，而await就是一个then的语法糖，并不会捕获异常, 那就需要使用try/catch来捕获异常，并进行相应的逻辑处理。
 * @param promise
 * @returns {Promise}
 */
function to(promise) {
    return promise.then(data => [null, data])
        .catch(err => [err])
}

async function catchError() {
    // 方式一，使用try catch
    try {
        let r1 = await errorPromise();
        console.log(r1);
    } catch (error) {
        console.log('=>使用try catch捕获异常', error.message);
    }

    // 方式二，使用await-to-js模块
    let [err, data] = await to(errorPromise())
    if (err) {
        console.log('=>使用await-to-js捕获异常', err.message);
    }
    else {
        console.log(data);
    }

    //方式三，await命令后面是一个Promise对象，直接可以使用.catch来捕获异常
    let r2 = await errorPromise().catch(e => console.log('=>使用await-to-js捕获异常', e.message))
    if (r2) {
        console.log(r2);
    }
}
catchError();

// ---------------end   await捕捉异常 ------------